﻿using Microsoft.EntityFrameworkCore;
using Sdari.Api.DbModel;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Sdari.Api.Services
{
    internal class TokenService : ITokenService
    {
        /// <summary>
        /// token有效期
        /// </summary>
        private const int DefaultTokenExpireSeconds = 7200;

        private readonly ITokenServiceContext _db;
        private const string LogCreator = "System";

        public TokenService(ITokenServiceContext context)
        {
            _db = context;
        }

        /// <summary>
        /// 获取token
        /// </summary>
        /// <param name="clientId">用户ClientId</param>
        /// <param name="secret">用户的Secret</param>
        /// <returns>返回token和剩余的有效时间</returns>
        public (Guid token, int? expire)? GetToken(string clientId, string secret)
        {
            var temp = (
                from c in _db.ConsumerInfo
                where c.ClientId == clientId & c.Secret == secret
                select c);
            var consumerList = temp.ToList();
            //用户是否合法
            if (consumerList.Count > 0)
            {
                var c = consumerList[0];
                var now = DateTime.Now;
                var tokenList = (
                    from tk in _db.TokenInfo
                    where tk.ConsumerUid == c.Uid & tk.Expire > now
                    select tk).ToList();
                //存在未过期token，返回之
                if (tokenList.Count > 0)
                {
                    return (tokenList[0].Token, (int?)tokenList[0].Expire?.Subtract(now).TotalSeconds);
                }
                //没有未过期token，新建
                else
                {
                    var token = Guid.NewGuid();
                    var tokenInfo = new TokenInfo()
                    {
                        ConsumerUid = c.Uid,
                        CreateDate = DateTime.Now,
                        Creator = "sys",
                        Expire = now.AddSeconds(DefaultTokenExpireSeconds),
                        Token = token
                    };
                    _db.TokenInfo.Add(tokenInfo);
                    _db.SaveChanges();
                    return (token, DefaultTokenExpireSeconds);
                }
            }
            //用户不合法
            else
                return null;
        }

        /// <summary>
        /// 验证token
        /// </summary>
        /// <param name="token">token</param>
        /// <returns>验证是否通过</returns>
        public bool VerifyToken(Guid token, string apiAdress, byte[] ipAdress)
        {
            var res = false;
            //没有找到api信息，返回失败
            var resApi = (
                from api in _db.ApiInfo
                where api.Address == apiAdress
                select api).FirstOrDefault();
            if (resApi == null)
                return res;
            //有api就可以记录访问情况
            var log = new CallingLog()
            {
                ApiUid = resApi.Uid,
                Ip = ipAdress,
                CreateDate = DateTime.Now,
                Creator = LogCreator,
            };
            //没有找到token，返回失败
            var resToken = (
                from tk in _db.TokenInfo
                where tk.Token == token
                select tk).FirstOrDefault();
            if (resToken == null)
            {
                log.Result = VerifyResult.InvalidToken;
            }
            else
            {
                var consumer = resToken.ConsumerUid;
                log.ConsumerUid = consumer;
                //token超时，返回失败
                if (resToken.Expire < DateTime.Now)
                {
                    log.Result = VerifyResult.TokenExpired;
                }
                else
                {
                    //没有访问权限，返回失败
                    var res2 = (
                        from acc in _db.ApiConsumerAccess
                        where acc.ConsumerUid == consumer && acc.ApiUid == resApi.Uid
                        select acc.Accessibility).FirstOrDefault();
                    //符合要求，返回成功
                    if (res2 == 1)
                    {
                        log.Result = VerifyResult.Success;
                        res = true;
                    }
                    else
                    {
                        log.Result = VerifyResult.AccessDeny;
                    }
                }
            }
            _db.CallingLog.Add(log);
            _db.SaveChanges();
            return res;
        }

        /// <summary>
        /// 严格验证token
        /// </summary>
        /// <param name="token">token</param>
        /// <returns>验证是否通过</returns>
        public bool VerifyToken(Guid token, Guid consumerUid)
        {
            var result = (
                from tk in _db.TokenInfo
                where tk.Token == token
                    && tk.ConsumerUid == consumerUid
                    && tk.Expire > DateTime.Now
                select tk).ToList();
            //没有或者超过一条记录都是不正确
            if (result.Count == 1)
                return true;
            return false;
        }

        /// <summary>
        /// 检查白名单
        /// </summary>
        /// <param name="apiAdress">api地址</param>
        /// <param name="whiteList">白名单值</param>
        /// <returns></returns>
        public bool WhiteListCheck(string apiAdress, IList<(string val, string type)> whiteList, byte[] ip)
        {
            var list = _db.vwWhiteList.Where(w => (w.Address == apiAdress || w.Address == "*") && w.IsValid == true).ToList();
            foreach (var (v, t) in whiteList)
            {
                foreach (var l in list)
                {
                    //Todo 尝试通过地址进行模式匹配
                    //var ip = System.Net.IPAddress.Parse(v);
                    //var mask = System.Net.IPAddress.Parse("0.0.0.255");
                    //var c = System.Net.IPAddress.Parse("127.0.0.1");
                    //var test1 = new System.Net.IPAddress(ip.Address & mask.Address);
                    //var test2 = new System.Net.IPAddress(test1.Address | c.Address);

                    //有一项 通过就算通过
                    if (l.Value.ToLower() == v.ToLower() && l.Type.ToLower() == t.ToLower())
                    {
                        _db.CallingLog.Add(new CallingLog
                        {
                            ApiUid = l.ApiUid,
                            WhiteListUid = l.Uid,
                            Ip = ip,
                            Result = VerifyResult.Success,
                            CreateDate = DateTime.Now,
                            Creator = LogCreator
                        });
                        _db.SaveChanges();
                        return true;
                    }
                }
            }
            return false;
        }
    }
}
